﻿using System.Collections.Generic;
using System.Linq;
using FizzBuzz.OutputProviders;

namespace FizzBuzz
{
    public class FizzBuzzLogic
    {
        private readonly IOutputProvider outputProvider;

        public FizzBuzzLogic(IOutputProvider outputProvider)
        {
            this.outputProvider = outputProvider;
        }

        public bool CalculateValues()
        {
            return FizzBuzz(1, 100, new List<Divisor>
                                        {
                                            new Divisor {Value = 3, Text = "Fizz"},
                                            new Divisor {Value = 5, Text = "Buzz"}
                                        });
        }


        public bool CalculateValues(int lowerRange, int upperRange, IEnumerable<Divisor> divisors)
        {
            return FizzBuzz(lowerRange, upperRange, divisors);
        }

        private bool FizzBuzz(int lowerRange, int upperRange, IEnumerable<Divisor> divisors)
        {
            if (
                !HasNumberWithinRange(lowerRange, upperRange) ||
                !HasValidSetOfDivisors(divisors) ||
                outputProvider == null) return false;


            for (var i = lowerRange; i <= upperRange; i++)
            {
                var message = string.Empty;

                if (IsDivisibleByAllDivisors(i, divisors, out message))
                {
                    outputProvider.Render(i, message);
                }
                else if (IsDivisibleByAnyDivisor(i, divisors, out message))
                {
                    outputProvider.Render(i, message);
                }
                else
                {
                    outputProvider.Render(i, i.ToString());
                }
            }


            return true;
        }

        private bool HasValidSetOfDivisors(IEnumerable<Divisor> divisors)
        {
            return (divisors != null && divisors.Any() && !divisors.Any(x => x.Value == 0));
        }
        private bool HasNumberWithinRange(int lowerRange, int upperRange)
        {
            return !(lowerRange > upperRange);
        }

        private bool IsDivisibleByAnyDivisor(int dividend, IEnumerable<Divisor> divisors, out string message)
        {
            foreach (var divisor in divisors)
            {
                if (dividend % divisor.Value == 0)
                {
                    message = divisor.Text;
                    return true;
                }
            }

            message = "";

            return false;
        }

        private bool IsDivisibleByAllDivisors(int dividend, IEnumerable<Divisor> divisors, out string message)
        {
            var messageResult = new List<string>();

            foreach (var divisor in divisors)
            {
                if (dividend % divisor.Value != 0)
                {
                    message = "";
                    return false;
                }

                if (!messageResult.Contains(divisor.Text))
                {
                    messageResult.Add(divisor.Text);
                }
            }

            message = string.Join(" ", messageResult.ToArray());

            return true;
        }
    }
}